import Base from './base.js'
import Gan from './gan.js'
import Zhi from './zhi.js'
import Ganzhi from './ganzhi.js'
import Bagua from './bagua.js'

import dictionary from './dictionary.js'

import utils from '@/utils/index.js'

// 人盘 天盘 地盘
const mountainTypes = ['human', 'ground', 'sky']
// 最大角度
const MAX_ANGLE = 360
// 地盘、人盘、天盘错位角度
const PAN_DELTA = 7.5
// 每一个山向的范围
const MOUNTAIN_RANGE = 15
// 山向结尾角度小数点精度
const RANGE_TAIL = 0.01

const mountainList = ['癸',
  '丑', '艮', '寅', '甲', '卯', '乙',
  '辰', '巽', '巳', '丙', '午', '丁',
  '未', '坤', '申', '庚', '酉', '辛',
  '戌', '乾', '亥', '壬', '子']
const mountainWuxing = [4,
  2, 2, 0, 0, 0, 0,
  2, 0, 1, 1, 1, 1,
  2, 2, 3, 3 ,3, 3,
  2, 3, 4, 4, 4
]
const mountainYinyang = [1,
  0, 0, 1, 1, 0, 1,
  1, 0, 0, 0, 1, 0,
  0, 1, 1, 0, 0, 0,
  1, 1, 0, 1, 1
]
// 山向纳甲八卦
const najiaList = ['坎',
  '兑', '艮', '离', '乾', '震', '坤',
  '坎', '巽', '兑', '艮', '离', '兑',
  '震', '坤', '坎', '震', '兑', '巽',
  '离', '乾', '震', '离', '坎']

// 山向对应的八卦
const mountainPalacesName = ['坎', '艮', '震', '巽', '离', '坤', '兑', '乾']
// 山向对应八卦的黄泉煞
const mountainPalacesHuangquan = ['辰', '寅', '申', '酉', '亥', '卯', '巳', '午']

// 透地龙范围
const DRAGON_NUM = 60
const DRAGON_RANGE = Math.floor(MAX_ANGLE / DRAGON_NUM)
// 透地龙分段从壬子1.5度的壬子开始
const DRAGON_START = 1.5

// 人盘三元范围
const NUM_RANGE = Math.floor(MAX_ANGLE / (mountainList.length * 3))
// 人盘三元从0度开始
const NUM_START = 0

// 山向描述文字列表
let dragonSections = null

class Mountain extends Base {
  /*
    构造函数
    mountain 为 int，则用索引初始化
    mountain 为 字符串，则用角度初始化
  */
  constructor (mountain = 0, options = {}) {
    super(options)
    
    this.initMountain(mountain)
  }
  
  initMountain (mountain = 0) {
    this.types.push('Mountain')
    
    if (typeof mountain === 'number') {
      this.index = Base.solve(mountainList, mountain)
    }
    else if (typeof mountain === 'string' && utils.str.isNumberString(mountain)){
      const angle = parseFloat(mountain)
      if (angle < 0 || angle >= MAX_ANGLE) {
        this.index = null
      }
      else {
        this.index = Math.floor(angle / MOUNTAIN_RANGE)
        this.angle = angle
      }
    }
    else {
      this.index = null
    }
    
    if (this.index === null) {
      this.enabled = false
      return
    }
    
    // 如果是根据索引确定的山向，则角度锁定为人盘起始位置
    if (!this.hasOwnProperty('angle')) {
      this.angle = (this.index * MOUNTAIN_RANGE) % MAX_ANGLE
    }
    
    this.name = mountainList[this.index]
    this.nameList = mountainList
    this.yinyang = mountainYinyang[this.index]
    this.wuxing = mountainWuxing[this.index]
    // 纳甲八卦
    this.najiaBagua = new Bagua(najiaList[this.index], {
      description: this.description + dictionary.BAGUA
    })
    
    // 将这个度数对应的山向信息全部打开
    this.initExtra()
  }
  
  // 详细解析此山向
  initExtra () {
    // 解析三盘
    this.initAllMountains()
    // 解析透地龙
    this.initGroundDragon()
    // 解析阴阳局数
    this.initNum()
    // 解析黄泉煞
    this.initHuangquan()
    
    // 解析山向透地龙分段
    this.initDragonSection()
  }
  
  // 解析三盘 
  initAllMountains () {
    if (!this.options.allMountains && !this.options.allDetail) { return }
    if (!this.humanMountain) {
      this.humanMountain = Mountain.getMountainDetailFromAngle(this.angle, mountainTypes[0])
    }
    if (!this.groundMountain) {
      this.groundMountain = Mountain.getMountainDetailFromAngle(this.angle, mountainTypes[1])
    }
    if (!this.skyMountain) {
      this.skyMountain = Mountain.getMountainDetailFromAngle(this.angle, mountainTypes[2])
    }
  }
  
  // 解析透地龙
  initGroundDragon () {
    if (!this.options.groundDragon && !this.options.allDetail) { return }
    
    // 六十龙计算要以地盘为准
    if (!this.groundMountain) {
      // 若没有已知的地盘信息，要重新计算一个
      this.groundMountain = Mountain.getMountainDetailFromAngle(this.angle, mountainTypes[1])
    }
    const groundMountain = this.groundMountain
    
    const ganzhi = Mountain.getGroundDragon(this.angle, groundMountain)
    
    this.groundDragon = new Ganzhi(ganzhi[0], ganzhi[1], null, {
      description: this.description + dictionary.GROUNDDRAGON
    })
  }
  
  // 解析上中下三元与局数
  initNum () {
    if (!this.options.hasNum && !this.options.allDetail) { return }
    
    if (!this.humanMountain) {
      this.humanMountain = Mountain.getMountainDetailFromAngle(angle, mountainTypes[0])
    }
    
    const numData = Mountain.getNumData(this.angle, this.humanMountain)

    
    this.numData = {
      // 阴阳
      isSolar: numData.isSolar,
      // 上/中/下元
      numOffset: numData.numOffset,
      // 三元数
      num: numData.num,
      // 角度范围
      range: {
        start: this.humanMountain.start + numData.numOffset * NUM_RANGE,
        end: (this.humanMountain.start + (numData.numOffset + 1) * NUM_RANGE + MAX_ANGLE - RANGE_TAIL) % MAX_ANGLE,
      }
    }
  }
  
  // 获取透地龙所在分段
  initDragonSection () {
    if (!this.options.dragonSection && !this.options.allDetail) { return }
    
    if (!Mountain.dragonSections) {
      Mountain.initDragonSections()
    }
    const sections = Mountain.dragonSections
    const len = sections.length
    let index = 0
    for (let i = 0; i < len; i++) {
      if (sections[i] <= this.angle && (sections[(i + 1) % len] > this.angle || i + 1 >= len)) {
        index = i
        break
      }
    }
    this.dragonSection = {
      start: sections[index],
      end: (sections[(index + 1) % len] + MAX_ANGLE - RANGE_TAIL) % MAX_ANGLE    
    }
  }
  
  // 解析黄泉煞
  initHuangquan () {
    if (!this.options.huangquan && !this.options.allDetail) { return }
    
    // 先确认山向的落宫
    const len = mountainList.length
    const newIndex = Math.floor(((this.index + 2) % len) / 3)
    // 获取落宫名称
    this.palaceName = mountainPalacesName[newIndex]
    
    // 黄泉煞
    this.huangquan = new Zhi(mountainPalacesHuangquan[newIndex], {
      description: this.description + dictionary.HUANGQUAN
    })
  }
  
  /* @section get 获取信息 */
  getZhiMountain () {
    return Mountain.getZhiMountain(this.index)
  }
  
  getGanMountain () {
    return Mountain.getGanMountain(this.index)
  }
  
  /* @section static 静态方法 */  
  // 根据角度和取向方式，获取角度范围，索引值等全部信息
  static getMountainDetailFromAngle (angle, mountainType = mountainTypes[0]) {
    const mountainTypeIndex = Mountain.getMountainTypeIndex(mountainType)
    const tempAngle = (angle + MAX_ANGLE - mountainTypeIndex * PAN_DELTA) % MAX_ANGLE
    const index = Math.floor(tempAngle / MOUNTAIN_RANGE)
    return {
      index: index,
      start: (index * MOUNTAIN_RANGE + mountainTypeIndex * PAN_DELTA) % MAX_ANGLE,
      end: ((index + 1) * MOUNTAIN_RANGE + mountainTypeIndex * PAN_DELTA - RANGE_TAIL) % MAX_ANGLE
    }
  }
  
  // 获取山向盘索引，0人盘 1地盘 2天盘
  static getMountainTypeIndex (mountainType = mountainTypes[0]) {
    const index = mountainTypes.indexOf(mountainType)
    if (index < 0) { return 0 }
    return index
  }
  
  // 获取某一个角度所在宫位的起始角度
  static getPalaceAngleRange (angle) {
    // 一个宫位有三个山向
    const PALACE_RANGE = MOUNTAIN_RANGE * 3
    let result = {}
    result.start = Mountain.standardAngle(Math.floor(((angle + 2 * MOUNTAIN_RANGE) % MAX_ANGLE) / PALACE_RANGE) * PALACE_RANGE - 2 * MOUNTAIN_RANGE)
    result.end = Mountain.standardAngle(result.start + PALACE_RANGE - RANGE_TAIL)
    return result
  }
  
  // 获取某一个角度对面的角度
  static getOppositeAngle (angle) {
    return (angle + MAX_ANGLE / 2) % MAX_ANGLE
  }
  
  // 获取某一个山向对面的山向索引值
  static getOppositeMountain (index) {
    const len = mountainList.length
    return (index + Math.floor(len / 2)) % len
  }
  
  // 获取另一个双山五行的名称或索引
  static getAnotherMountain (index) {
    const newIndex = index % 2 === 0 ? index + 1 : index - 1
    return mountainList[newIndex]
  }
  
  // 双山五行获取地支名称
  static getZhiMountain (index) {
    const newIndex = index % 2 === 0 ? index + 1 : index
    return mountainList[newIndex]
  }
  
  // 双山五行获取天干名称
  static getGanMountain (index) {
    const newIndex = index % 2 === 0 ? index : index - 1
    if (newIndex === 2 || newIndex === 8 || newIndex === 14 || newIndex === 20) {
      // 四个卦，阳卦化为戊，阴卦化为己
      return mountainYinyang[newIndex] ? '戊' : '己'
    }
    return mountainList[newIndex]
  }
  
  // 计算两个山向角度在顺时针方向上a - b的差值
  // 罗盘的顺时针方向角度不断变大
  // 这种计算方式，使得结果永远是正数
  static angleSub (a, b) {
    return (a + MAX_ANGLE - b) % MAX_ANGLE
  }
  
  // 角度标准化
  static standardAngle (angle) {
    while (angle < 0) {
      angle = angle + MAX_ANGLE
    }
    while (angle >= MAX_ANGLE) {
      angle = angle - MAX_ANGLE
    }
    return angle
  }
  
  /* 
    计算透地龙
    angle 角度
    groundMountain 地盘山向信息
  */
  static getGroundDragon (angle, groundMountain) {    
    const len = mountainList.length
    const zhiName = Mountain.getZhiMountain(groundMountain.index)
    const zhiMountainIndex = mountainList.indexOf(zhiName)
    // 决定阴阳干分组
    const groupIndex = Math.floor(((groundMountain.index + len + 2) % len) / 2)
    // 决定阴干还是阳干，偶数为阳干
    const offset = groupIndex % 2 === 0 ? 0 : 1
    // 天干分组
    const ganGroupIndex = Math.floor(groupIndex / 2)
    const ganLen = Gan.ganList.length
    const ganStartIndex = ((ganGroupIndex * 2) + offset) % ganLen
    // 计算天干偏移
    const angleStart = zhiMountainIndex === groundMountain.index ? (groundMountain.start - MOUNTAIN_RANGE) % MAX_ANGLE : groundMountain.start
    const angleDelta = Mountain.angleSub(angle, angleStart)
    const ganOffset = Math.floor(angleDelta / DRAGON_RANGE) * 2
    // 天干索引
    const ganIndex = (ganStartIndex + ganOffset) % ganLen
    const ganName = Gan.ganList[ganIndex]
    return ganName + zhiName
  }
  
  /*
    获取上中下三元信息
    angle 角度
    humanMountain 人盘山向信息
  */
  static getNumData (angle, humanMountain) {
    // 每一元数字增加或减少6
    const DELTA_NUM = 6
    
    // 山向对应宫的后天八卦数起算，用于确认上元
    const upStart = [1, 8, 3, 4, 9, 2, 7, 6]
    const limitLen = upStart.length + 1
    
    const index = humanMountain.index
    // 巳到乾为阴，亥到旬为阳
    const isSolar = !(index >= 9 && index <= 20)
    
    const len = mountainList.length
    const groupIndex = Math.floor(((index + len + 2) % len) / 3)
    let groupOffset = ((index + len + 2) % len) % 3
    // 坎艮震巽，偏移量为正，造就了巳亥两个异类
    groupOffset = groupIndex < Math.floor(upStart.length / 2) ? groupOffset : groupOffset * (-1)
    
    // 人盘角度必须获取
    if (!this.humanMountain) {
      this.humanMountain = Mountain.getMountainDetailFromAngle(angle, mountainTypes[0])
    }
    
    const angleDelta = Mountain.angleSub(angle, humanMountain.start)
    const numOffset = Math.floor(angleDelta / NUM_RANGE)
    const deltaOffset = isSolar ? numOffset : numOffset * (-1)
    
    // 上元就是从山向对应宫的后天八卦数起算
    let curNum = (upStart[groupIndex] + groupOffset + limitLen - 1) % limitLen + 1
    curNum = (curNum + deltaOffset * DELTA_NUM + limitLen * 2 - 1) % limitLen + 1
    
    return {
      // 阴阳
      isSolar,
      // 上/中/下元
      numOffset,
      // 三元数
      num: curNum
    }
  }
  
  // 对360度进行分段
  // 分段产生的原因是六十地龙6度一段，人盘三元5度一段产生的交错
  static initDragonSections () {
    if (!Mountain.dragonSections) {
      Mountain.dragonSections = []
    }
    else { return }
    
    // 透地龙分段从壬子1.5度的壬子开始，人盘三元从0开始
    let dragonAngle = DRAGON_START
    let numAngle = NUM_START
    
    while (numAngle < MAX_ANGLE || dragonAngle < MAX_ANGLE) {
      if (numAngle < dragonAngle) {
        Mountain.dragonSections.push(numAngle)
        numAngle = numAngle + NUM_RANGE
      }
      else {
        Mountain.dragonSections.push(dragonAngle)
        dragonAngle = dragonAngle + DRAGON_RANGE
      }
    }
  }
}

Object.assign(Mountain, {
  mountainList,
  mountainTypes,
  dragonSections,
  // 范围
  MOUNTAIN_RANGE,
  NUM_RANGE,
  DRAGON_RANGE,
  // 其他数据
  RANGE_TAIL,
  MAX_ANGLE
})

export default Mountain